package edu.math.chaos.itfunc.api;

/**
 * User: Илья
 * Date: 08.11.13
 * Time: 13:06
 */
public class Parameters {

	private double iterationThousands;
	private int width;
	private int height;
	private int initX;
	private int initY;

	public void setIterationThousands(double iterationThousands) {
		this.iterationThousands = iterationThousands;
	}

	public int getIterations() {
		return (int) Math.round(iterationThousands * 1e+3);
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getWidth() {
		return width;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getHeight() {
		return height;
	}

	public void setInitX(int initX) {
		this.initX = initX;
	}

	public int getInitX() {
		return initX;
	}

	public void setInitY(int initY) {
		this.initY = initY;
	}

	public int getInitY() {
		return initY;
	}

	public ErrorInfo checkParameters() {
		if (iterationThousands <= 0) {
			return new ErrorInfo(ParameterType.ITERATION_THOUSANDS, "Неверное число тысяч итераций");
		}

		if (width <= 0) {
			return new ErrorInfo(ParameterType.WIDTH, "Неверная ширина области");
		}

		if (height <= 0) {
			return new ErrorInfo(ParameterType.HEIGHT, "Неверная высота области");
		}

		if (!checkCoordinate(initX, width)) {
			return new ErrorInfo(ParameterType.INIT_X, "x-координата за границей области");
		}

		if (!checkCoordinate(initY, height)) {
			return new ErrorInfo(ParameterType.INIT_Y, "y-координата за границей области");
		}

		return null;
	}

	private boolean checkCoordinate(int coordinate, int upperBound) {
		return coordinate >= 0 && coordinate < upperBound;
	}

	public static enum ParameterType {

		ITERATION_THOUSANDS {

			protected ErrorInfo doSetParameter(String value, Parameters params) {
				try {
					params.setIterationThousands(Double.parseDouble(value));
				} catch (NumberFormatException e) {
					return new ErrorInfo(this, DOUBLE_CONVERSION_ERROR);
				}

				return null;
			}
		},

		WIDTH {

			protected ErrorInfo doSetParameter(String value, Parameters params) {
				try {
					params.setWidth(Integer.parseInt(value));
				} catch (NumberFormatException e) {
					return new ErrorInfo(this, INTEGER_CONVERSION_ERROR);
				}

				return null;
			}
		},

		HEIGHT {

			protected ErrorInfo doSetParameter(String value, Parameters params) {
				try {
					params.setHeight(Integer.parseInt(value));
				} catch (NumberFormatException e) {
					return new ErrorInfo(this, INTEGER_CONVERSION_ERROR);
				}

				return null;
			}
		},

		INIT_X {

			protected ErrorInfo doSetParameter(String value, Parameters params) {
				try {
					params.setInitX(Integer.parseInt(value));
				} catch (NumberFormatException e) {
					return new ErrorInfo(this, INTEGER_CONVERSION_ERROR);
				}

				return null;
			}
		},

		INIT_Y {

			protected ErrorInfo doSetParameter(String value, Parameters params) {
				try {
					params.setInitY(Integer.parseInt(value));
				} catch (NumberFormatException e) {
					return new ErrorInfo(this, INTEGER_CONVERSION_ERROR);
				}

				return null;
			}
		};

		public ErrorInfo setParameter(String value, Parameters params) {
			if (value == null) {
				return new ErrorInfo(this, "Не задано строковое значение параметра");
			}

			if (params == null) {
				return new ErrorInfo(this, "Не задана целевая коллекция параметров");
			}

			return doSetParameter(value, params);
		}

		protected abstract ErrorInfo doSetParameter(String value, Parameters params);

		private static final String DOUBLE_CONVERSION_ERROR = "Невозможно преобразовать к числу";
		private static final String INTEGER_CONVERSION_ERROR = "Невозможно преобразовать к целому числу";
	}

	public static class ErrorInfo {

		private ParameterType parameterType;
		private String message;

		public ErrorInfo(ParameterType parameterType, String message) {
			this.parameterType = parameterType;
			this.message = message;
		}

		public ParameterType getParameterType() {
			return parameterType;
		}

		public String getMessage() {
			return message;
		}
	}
}
